﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using DTO = Microsoft.MSE.Catalog.Contracts;
using System.Xml;

namespace Microsoft.MSE.Catalog.Module.Data
{
    public class Channel
    {
        public Channel()
        {

        }

        #region Channel Functions

        public static Guid CreateChannel(DTO.Channel chnl, SqlTransaction txn, SqlConnection conn)
        {
            Guid ID;
            bool autoclose = false;
            const string sprocname = "[ServiceEngine].[CreateChannel]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@ID", SqlDbType.UniqueIdentifier);
                    SqlParameter NameParam = new SqlParameter("@Name", SqlDbType.NVarChar, 400);
                    SqlParameter MonikerSchemaParam = new SqlParameter("@MonikerSchema", SqlDbType.Xml);
                    SqlParameter BindingIDParam = new SqlParameter("@BindingID", SqlDbType.UniqueIdentifier);
                    SqlParameter EditorAssemblyParam = new SqlParameter("@EditorAssembly", SqlDbType.NVarChar, 4000);
                    SqlParameter EditorTypeParam = new SqlParameter("@EditorTypeName", SqlDbType.NVarChar, 4000);
                    SqlParameter DescriptionParam = new SqlParameter("@Description", SqlDbType.NText);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Direction = ParameterDirection.Output;
                    NameParam.Value = chnl.Name;
                    if (chnl.MonikerSchema != null)
                    {
                        MonikerSchemaParam.Value = chnl.MonikerSchema.OuterXml;
                    }
                    else
                    {
                        throw new ApplicationException("CreateChannel failed: MonikerSchema is a required element but was not sent");
                    }
                    BindingIDParam.Value = chnl.Binding.ID;
                    EditorAssemblyParam.Value = chnl.MonikerEditorAssemblyName;
                    EditorTypeParam.Value = chnl.MonikerEditorTypeName;
                    DescriptionParam.Value = chnl.Description;

                    cmd.Parameters.Add(IDParam);
                    cmd.Parameters.Add(NameParam);
                    cmd.Parameters.Add(MonikerSchemaParam);
                    cmd.Parameters.Add(BindingIDParam);
                    cmd.Parameters.Add(EditorAssemblyParam);
                    cmd.Parameters.Add(EditorTypeParam);
                    cmd.Parameters.Add(DescriptionParam);

                    cmd.ExecuteNonQuery();

                    ID = (Guid)IDParam.Value;
                    //Assign Policies SGK
                    if (chnl.Policies != null)
                    {
                        foreach (DTO.Policy pol in chnl.Policies)
                        {
                            if (CatalogSvcHelper.IsGuidNullOrEmpty(pol.ID.ToString()))
                            {
                                pol.ID = Policy.CreatePolicy(pol, txn, conn);
                            }
                            Policy.AssignEntityPolicy(ID, pol.ID, txn, conn);
                        }
                    }
                }

            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection in opened in this method
            }

            return ID;
        }

        public static DTO.Channel UpdateChannel(DTO.Channel chnl, SqlTransaction txn, SqlConnection conn)
        {
            Guid ID;
            bool autoclose = false;
            const string sprocname = "[ServiceEngine].[UpdateChannel]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                #region Change Tracking
                //get list of runtimes affected
                List<DTO.DBEventDataItem> eventData = null;
                try
                {
                    //eventData = RuntimeServer.ListRuntimeServersForChannelID(chnl.ID, txn, conn);
                }
                catch (System.Exception ex)
                {
                    TraceHelper.TraceError("Error collecting affected runtimes in UpdateChannel.  No change notifications will be sent. Error [{0}].", CatalogSvcHelper.GetExceptionDetails(ex));
                }

                #endregion

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@ID", SqlDbType.UniqueIdentifier);
                    SqlParameter NameParam = new SqlParameter("@Name", SqlDbType.NVarChar, 400);
                    SqlParameter MonikerSchemaParam = new SqlParameter("@MonikerSchema", SqlDbType.Xml);
                    SqlParameter BindingIDParam = new SqlParameter("@BindingID", SqlDbType.UniqueIdentifier);
                    SqlParameter EditorAssemblyParam = new SqlParameter("@EditorAssembly", SqlDbType.NVarChar, 4000);
                    SqlParameter EditorTypeParam = new SqlParameter("@EditorTypeName", SqlDbType.NVarChar, 4000);
                    SqlParameter DescriptionParam = new SqlParameter("@Description", SqlDbType.NText);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = chnl.ID;
                    NameParam.Value = chnl.Name;
                    if (chnl.MonikerSchema != null)
                    {
                        MonikerSchemaParam.Value = chnl.MonikerSchema.OuterXml;
                    }
                    else
                    {
                        throw new ApplicationException("UpdateChannel failed: MonikerSchema is a required element but was not sent");
                    }
                    BindingIDParam.Value = chnl.Binding.ID;
                    EditorAssemblyParam.Value = chnl.MonikerEditorAssemblyName;
                    EditorTypeParam.Value = chnl.MonikerEditorTypeName;
                    DescriptionParam.Value = chnl.Description;

                    cmd.Parameters.Add(IDParam);
                    cmd.Parameters.Add(NameParam);
                    cmd.Parameters.Add(MonikerSchemaParam);
                    cmd.Parameters.Add(BindingIDParam);
                    cmd.Parameters.Add(EditorAssemblyParam);
                    cmd.Parameters.Add(EditorTypeParam);
                    cmd.Parameters.Add(DescriptionParam);

                    cmd.ExecuteNonQuery();

                    ID = (Guid)IDParam.Value;
                    //Assign Policies SGK
                    Policy.DeletePoliciesFromEntity(chnl.ID, txn, conn);

                    if (chnl.Policies != null)
                    {
                        foreach (DTO.Policy pol in chnl.Policies)
                        {
                            Policy.AssignEntityPolicy(chnl.ID, pol.ID, txn, conn);
                        }
                    }
                }

                //send notification to affected runtimes
                RepositoryChange.Save(DTO.DBEvents.OperationsUpdated, DTO.DBChangeType.Update, eventData, txn, conn);

            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection in opened in this method
            }

            return chnl;
        }

        public static void DeleteChannel(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            const string sprocname = "[ServiceEngine].[DeleteChannel]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                //NOTE: No change tracking implemented here since db integrity requires removing operation versions first

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@ID", SqlDbType.UniqueIdentifier);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = ID;
                    cmd.Parameters.Add(IDParam);

                    cmd.ExecuteNonQuery();
                }

                //Delete all entity Policies SGK
                Policy.DeletePoliciesFromEntity(ID, txn, conn);

            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection in opened in this method
            }
        }

        public static DTO.Channel GetChannel(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            DTO.Channel chnl = new DTO.Channel();
            const string sprocname = "[ServiceEngine].[GetChannel]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@ID", SqlDbType.UniqueIdentifier);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = ID;
                    cmd.Parameters.Add(IDParam);

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            reader.Read();
                            chnl.ID = reader.GetGuid(0);
                            chnl.Name = reader.GetString(1);
                            XmlDocument xdoc = new XmlDocument();
                            xdoc.Load(reader.GetSqlXml(2).CreateReader());
                            chnl.MonikerSchema = xdoc.DocumentElement;
                            chnl.Binding = CatalogSvcHelper.AssembleBindingFromBindingElements(Binding.GetMSEBinding(reader.GetGuid(3), txn, conn));
                            xdoc = new XmlDocument();

                            chnl.MonikerEditorAssemblyName = reader.GetString(4);
                            chnl.MonikerEditorTypeName = reader.GetString(5);
                            if (reader.IsDBNull(6))
                            {
                                chnl.Description = null;
                            }
                            else
                            {
                                chnl.Description = reader.GetString(6);
                            }
                        }

                        //Get policies SGK 05/06/2008
                        chnl.Policies = Policy.GetEntityPolicies(chnl.ID, txn, conn);
                    }
                }
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection in opened in this method
            }

            return chnl;
        }

        public static DTO.ChannelList ListChannels(SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            DTO.ChannelList list = new DTO.ChannelList();
            const string sprocname = "[ServiceEngine].[ListChannels]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            list.Channels = new List<DTO.ChannelListElement>();
                            while (reader.Read())
                            {
                                DTO.ChannelListElement element = new DTO.ChannelListElement();
                                element.ID = reader.GetGuid(0);
                                element.Name = reader.GetString(1);
                                if (reader.IsDBNull(2))
                                {
                                    element.Description = null;
                                }
                                else
                                {
                                    element.Description = reader.GetString(2);
                                }
                                list.Channels.Add(element);
                            }
                        }
                    }
                }
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection in opened in this method
            }

            return list;

        }

        public static DTO.ChannelList ListChannelsForPolicyID(Guid policyID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            DTO.ChannelList list = new DTO.ChannelList();
            const string sprocname = "[ServiceEngine].[ListChannelsForPolicyID]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@PolicyID", SqlDbType.UniqueIdentifier);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = policyID;
                    cmd.Parameters.Add(IDParam);

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            list.Channels = new List<DTO.ChannelListElement>();
                            while (reader.Read())
                            {
                                DTO.ChannelListElement element = new DTO.ChannelListElement();
                                element.ID = reader.GetGuid(0);
                                element.Name = reader.GetString(1);
                                if (reader.IsDBNull(2))
                                {
                                    element.Description = null;
                                }
                                else
                                {
                                    element.Description = reader.GetString(2);
                                }
                                list.Channels.Add(element);
                            }
                        }
                    }
                }
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection in opened in this method
            }

            return list;

        }

        #endregion

        #region Channel Group functions

        public static List<DTO.ChannelListElement> ListChannelsByGroup(Guid entityGroupID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            List<DTO.ChannelListElement> list = new List<DTO.ChannelListElement>();
            const string sprocname = "[ServiceEngine].[ListEntitiesByGroup]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@entityGroupID", SqlDbType.UniqueIdentifier);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = entityGroupID;
                    cmd.Parameters.Add(IDParam);

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            list = new List<DTO.ChannelListElement>();
                            while (reader.Read())
                            {
                                DTO.ChannelListElement element = new DTO.ChannelListElement();
                                element.ID = reader.GetGuid(0);
                                element.Name = reader.GetString(1);
                                if (reader.IsDBNull(2))
                                {
                                    element.Description = null;
                                }
                                else
                                {
                                    element.Description = reader.GetString(2);
                                }

                                list.Add(element);
                            }
                        }
                    }
                }

            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection in opened in this method
            }

            return list;

        }

        #endregion
    }
}
